package cc.lucien.ioc;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author lucien-ma
 * @date 2021/9/5
 * @apiNote
 */
public class MyClassPathXmlApplicationContext {

    private Map<String, Object> iocMap;

    public MyClassPathXmlApplicationContext(String path) {
        iocMap = new HashMap<>();
        // 解析 XML
        parseXML(path);
    }

    public void parseXML(String path) {
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read("src/main/resources/" + path);
            Element root = document.getRootElement();
            Iterator<Element> rootIter = root.elementIterator();
            while (rootIter.hasNext()) {
                // 遍历父节点下的子节点，获取子节点
                Element bean = rootIter.next();
                // 拿到子节点的属性值
                String idStr = bean.attributeValue("id");
                String className = bean.attributeValue("class");
                // 反射动态创建对象
                Class clazz = Class.forName(className);
                // 获取无参构造方法
                Constructor constructor = clazz.getConstructor();
                // 通过无参构造创建对象
                Object object = constructor.newInstance();
                // 给属性赋值
                Iterator<Element> beanIter = bean.elementIterator();
                while (beanIter.hasNext()) {
                    // 获取子节点
                    Element property = beanIter.next();
                    // 获取子节点的属性值
                    String propertyName = property.attributeValue("name");
                    String propertyValue = property.attributeValue("value");
                    // 获取 setter 方法
                    // num --> setNum, brand --> setBrand
                    String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                    // 获取属性
                    Field field = clazz.getDeclaredField(propertyName);
                    // 获取属性的类型，也是setter方法中传参的类型
                    Class<?> type = field.getType();
                    Method method = clazz.getMethod(methodName, type);
                    // 类型转换
                    Object value = propertyValue;
                    switch (type.getName()) {
                        case "java.lang.Integer":
                            value = Integer.parseInt(propertyValue);
                            break;
                        case "java.lang.Double":
                            value = Double.parseDouble(propertyValue);
                            break;
                    }
                    // 调用方法
                    method.invoke(object, value);
                }
                // 存放到 map 集合中
                iocMap.put(idStr, object);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String name) {
        return iocMap.get(name);
    }

}
